`ifndef NewLabel_RF_AGENT_SV
`define NewLabel_RF_AGENT_SV

import ovm_pkg::*;
`include "ovm_macros.svh"
import	definitions::*;

interface RegFile_if(input logic clk, rst_n);

	logic [23:0]	data_i;
	logic [23:0]	data_o;
	logic [4:0]	addr_o;
	logic cs;
	logic wr;
	logic ack;
	logic err;

 	modport drv(
		input clk, rst_n,
		input data_i, ack, err,
		output data_o, addr_o, cs, wr
  	);
 	   
endinterface

////////////////////////// items /////////////////////////
class tr_rf extends ovm_sequence_item;
	rand logic[23:0] data;
	rand logic[4:0]	addr;
	rand Bus_OP_t op;
	rand int trans_delay;

	//constraint rf_con { }
	constraint trans_delay_con{trans_delay>=0; trans_delay<=30;}

	`ovm_object_utils_begin(tr_rf)
		`ovm_field_int(data, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(addr, OVM_ALL_ON + OVM_HEX)
		`ovm_field_enum(Bus_OP_t, op, OVM_ALL_ON + OVM_HEX)
		`ovm_field_int(trans_delay, OVM_ALL_ON + OVM_HEX)
	`ovm_object_utils_end

	function new (string name = "tr_rf");
		super.new(name);
	endfunction : new

	virtual function void do_print(ovm_printer printer);
		super.do_print(printer);
	endfunction : do_print
	
endclass : tr_rf

////////////////////////// drivers /////////////////////////
class NewLabel_rf_driver extends ovm_driver #(tr_rf);
	virtual RegFile_if.drv rfif;
	`ovm_component_utils(NewLabel_rf_driver)

	function new(string name, ovm_component parent);
		super.new(name, parent);
	endfunction : new

	function void assign_vi(virtual interface RegFile_if rfif);
	  this.rfif = rfif;     
	endfunction : assign_vi

	task run();
		init_signals();

		forever begin
		  seq_item_port.get_next_item(req);
		  $cast(rsp, req.clone());
		  //req.print();
//ovm_report_info("TRP", $psprintf("Detail:\n%s", req.sprint()), OVM_FULL);

		  drive_transfer(req);
		        
		  rsp.set_id_info(req);
		  seq_item_port.item_done(rsp);
		end
	endtask : run

	task init_signals();
		time run_delay;
		assert(get_config_int("run_delay", run_delay));
		#run_delay;
		wait(rfif.rst_n);
		rfif.data_o <= '0;
		rfif.addr_o <= '0;
		rfif.cs <= 1'b0;
		rfif.wr <= 1'b0;
	endtask : init_signals

	task drive_transfer(tr_rf trans);
		if(trans.trans_delay>0)
		begin
			#trans.trans_delay;    
		end
		ovm_report_info("rf config", "Driving...", OVM_HIGH);
	    ovm_report_info("rf config", $psprintf("Detail:\n%s", trans.sprint()), OVM_FULL);

	    @(posedge rfif.clk)
	    begin
	    	rfif.cs <= #1 1'b1;
	    	rfif.data_o <= #1 trans.data;
	    	rfif.addr_o <= #1 trans.addr;
	    	if(trans.op == WRITE)
	    		rfif.wr <= #1 1'b1;
		end

		@(posedge rfif.clk iff((rfif.ack===1'b1)|(rfif.err===1'b1)))
		begin
    	rfif.data_o <= #1 '0;
		rfif.addr_o <= #1 '0;
		rfif.cs <= #1 1'b0;
		rfif.wr <= #1 1'b0;
		end
	    
	endtask : drive_transfer
	
endclass : NewLabel_rf_driver

////////////////////////// sequencers /////////////////////////
class NewLabel_rf_sequencer extends ovm_sequencer #(tr_rf);
	`ovm_sequencer_utils(NewLabel_rf_sequencer)

	function new(string name, ovm_component parent=null);
		super.new(name, parent);
		`ovm_update_sequence_lib_and_item(tr_rf)
	endfunction

endclass : NewLabel_rf_sequencer

////////////////////////// sequences /////////////////////////
virtual class NewLabel_rf_base_sequence extends ovm_sequence #(tr_rf);  
	function new(string name="NewLabel_rf_base_seq");    
		super.new(name);  
	endfunction

	virtual task pre_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s pre_body() raising an ovm_test_done objection", 
					get_sequence_path()), OVM_MEDIUM);    
		ovm_test_done.raise_objection(this);  
	endtask  

	virtual task post_body();    
		m_sequencer.ovm_report_info(get_type_name(), 
					$psprintf("%s post_body() dropping an ovm_test_done objection",
					get_sequence_path()), OVM_MEDIUM);
		ovm_test_done.drop_objection(this);  
	endtask  
endclass : NewLabel_rf_base_sequence

class rf_config_seq extends NewLabel_rf_base_sequence;

  function new (string name = "rf_config_seq");
    super.new(name);
  endfunction : new
  
  `ovm_sequence_utils(rf_config_seq, NewLabel_rf_sequencer)

  tr_rf lli;
  
  virtual task body();
	`ovm_create(lli);
	lli.addr = 5'h01;
	lli.data = 24'b0000_0001_0000_0000_0001_0000;
	lli.op = WRITE;
	`ovm_send(lli);

	`ovm_create(lli);
	lli.addr = 5'h02;
	lli.data = 24'b0010_1000_0000_0000_0000_0000;
	lli.op = WRITE;
	`ovm_send(lli);

	`ovm_create(lli);
	lli.addr = 5'h00;
	lli.data = 24'b0000_0000_0000_0000_0000_0001;
	lli.op = WRITE;
	`ovm_send(lli);
  endtask : body

endclass : rf_config_seq


////////////////////////// AGENT /////////////////////////
class NewLabel_rf_agent extends ovm_agent;
  ovm_active_passive_enum is_active = OVM_ACTIVE;
  
  `ovm_component_utils_begin(NewLabel_rf_agent)
    `ovm_field_enum(ovm_active_passive_enum, is_active, OVM_ALL_ON)
  `ovm_component_utils_end
  
  NewLabel_rf_sequencer rf_seqr;
  NewLabel_rf_driver rf_driver;
  
  function new(string name, ovm_component parent=null);
    super.new(name, parent);
  endfunction
  
  virtual function void build();
    super.build();
    if (is_active == OVM_ACTIVE) 
    begin
      rf_seqr = NewLabel_rf_sequencer::type_id::create("rf_seqr",this);
      rf_driver = NewLabel_rf_driver::type_id::create("rf_driver",this);
    end
  endfunction : build
  
  virtual function void connect();
    super.connect();
    if(is_active == OVM_ACTIVE) 
    begin
      rf_driver.seq_item_port.connect(rf_seqr.seq_item_export);
    end
  endfunction : connect

  function void assign_vi(virtual interface RegFile_if bif);
  	if (is_active == OVM_ACTIVE)
  	begin
  		rf_driver.assign_vi(bif);
  	end
  endfunction : assign_vi
  
endclass : NewLabel_rf_agent


`endif // NewLabel_RF_AGENT_SV
